package queue

import (
	"context"
	"sync"
)

// 用 chan 控制阻塞唤醒的并发队列，最基础的并发、阻塞、唤醒控制，本质是在模仿 cond.Wait 方法
type ConcurrentBlockQueueV1[T any] struct {
	dequeSignal chan struct{}
	enqueSignal chan struct{}

	mu      sync.Mutex
	data    []T
	maxSize int
}

func NewConcurrentBlockQueueV1[T any](maxSize int) *ConcurrentBlockQueueV1[T] {
	return &ConcurrentBlockQueueV1[T]{
		data:        make([]T, 0, maxSize),
		maxSize:     maxSize,
		dequeSignal: make(chan struct{}),
		enqueSignal: make(chan struct{}),
	}
}

func (c *ConcurrentBlockQueueV1[T]) EnQueue(ctx context.Context, data T) error {
	c.mu.Lock()
	for c.IsFull() {
		c.mu.Unlock()

		// 解锁和 select 之间有一段时间空隙，可以发生很多事情

		select {
		case <-c.dequeSignal:
			// 这里被唤醒的 goroutin 还要和新的 goroutine 抢锁
			c.mu.Lock()
		case <-ctx.Done():
			return ctx.Err()
		}
	}

	c.data = append(c.data, data)
	c.mu.Unlock()

	select {
	case c.enqueSignal <- struct{}{}:
	default:
	}
	return nil
}
func (c *ConcurrentBlockQueueV1[T]) DeQueue(ctx context.Context) (T, error) {
	c.mu.Lock()
	for c.IsEmpty() {
		c.mu.Unlock()

		// 解锁和 select 之间有一段时间空隙，可以发生很多事情

		select {
		case <-c.enqueSignal:
			// 这里被唤醒的 goroutin 还要和新的 goroutine 抢锁
			c.mu.Lock()
		case <-ctx.Done():
			var t T
			return t, ctx.Err()
		}
	}
	data := c.data[0]
	c.data = c.data[1:]
	c.mu.Unlock()

	select {
	case c.dequeSignal <- struct{}{}:
	default:
	}
	return data, nil
}
func (c *ConcurrentBlockQueueV1[T]) IsFull() bool {
	return len(c.data) == c.maxSize
}
func (c *ConcurrentBlockQueueV1[T]) IsEmpty() bool {
	return len(c.data) == 0
}
func (c *ConcurrentBlockQueueV1[T]) Len() uint64 {
	return uint64(len(c.data))
}
